import React, { useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import cn from 'classnames';
import path from 'ramda/es/path';
import assocPath from 'ramda/es/assocPath';
import { gql, useQuery } from '@apollo/client';

import { withRouter } from 'react-router';
import { Redirect } from 'react-router-dom';
import jsonPatch from 'fast-json-patch';

// import Select, { Option } from '@kuiper-components/select';
import Select from '@knownsec/react-component/dist/Select'
import Breadcrumb, { Item } from '../../components/Breadcrumb';

import Loading from '@kuiper-components/loading';
import Tooltip from '@kuiper-components/tooltip';
import { Help32 as HelpIcon } from '@carbon/icons-react';

import { datetime } from '../../../common/formatter';
import Label from '../../components/Label';
import PortraitVis from './PortraitVis';
import MonitorCounts from './MonitorCounts';
import s from './SitePortrait.module.css';
import fillWarnings from './fillWarning';
import fetch from 'API/fetch'

const QUERY_GQL = gql`
	{
		BalanceOne {
			_id
			balance {
				sitePortrait {
					value
				}
			}
		}
	}
`;
function SitePortrait({
	className,
	preview,
	location: {
		search
	},
	history: { replace },
}) {
	const jobId = JSON.parse(
		new URLSearchParams(search).get('filter') || '{}'
	)?.jobId;
	const baseData = useQuery(QUERY_GQL, { errorPolicy: 'all' });

	const handleSelectJob = id => {
		replace(`/site-portrait?filter=${encodeURI(JSON.stringify({ jobId: id}))}`);
	};
	const [jobList, setJobList] = useState([])
	const getJobList = () => {
		fetch('/api/v2/site_portrait/target_list').then(res => {
			const jobs = res.data?.map(item => {
				const [label, value] = Object.entries(item)[0]
				return { label, value }
			})
			setJobList(jobs)
			if (!jobId && res?.data?.length > 0) {
				const jobId = jobs[0]?.value;
				if (jobId) {
					window.location.href = `/#/site-portrait?filter=${encodeURI(JSON.stringify({ jobId }))}`
				}
			}
		})
	}
	const [selectedPatchVersion, setSelectPatch] = React.useState(null)
	const [loading, setLoading] = useState(false)
	const [sitePortraitData, setSitePortraitData] = useState(null)
	const getSitePortrait = (version = selectedPatchVersion) => {
		setLoading(true)
		fetch('/api/v2/site_portrait/target?site_portrait_id=' + jobId, { method: 'GET' }).then(res => {
			const { site_portrait, tasks, warnings } = res?.data || {}
			const data = {
				site_portrait,
				tasks: tasks?.slice()?.sort(
					(a, b) => b?.version - a?.version
				),
				warnings
			}
			// const { SitePortraitOne: { createTime: created_at, portrait, jobId: id, target, version }, TaskMany: tasks} = sitePortrait?.data || {}
			// const data = {
			// 	site_portrait: { created_at, portrait, id, target, version, job: {} },
			// 	tasks: tasks?.map(item => {
			// 		const { endTime: created_at, _id: id, result: { addition: { patches, version }} } = item
			// 		return { created_at, id, patches, version }
			// 	})?.slice()?.sort(
			// 		(a, b) => b?.version - a?.version
			// 	)
			// }
			let patchVersion = version
			if (!version || data?.tasks?.filter(i => i.version === version).length === 0) {
				patchVersion = data?.tasks[0]?.version
				setSelectPatch(data?.tasks[0]?.version);
			}
			const patchSelectOptions = data?.tasks.filter(
				(t, i) =>
					i === 0 ||
					// 不显示没有修改页面的历史
					JSON.parse(t?.patches || '[]').some(
						x => x.path.indexOf('/children') >= 0
					)
			)
			let finallyData = JSON.parse(data?.site_portrait?.portrait || '{}');
			finallyData.value = data?.site_portrait?.target;
			finallyData.notPortrait = data?.site_portrait?.portrait === '{}';
			if (patchVersion !== 'latest') {
				const selectedVersion = parseInt(patchVersion, 10);
				const patches = data?.tasks
					.filter(x => x?.version >= selectedVersion)
					.sort(
						(a, b) =>
							new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
					)
					.map(x => {
						return JSON.parse(x?.patches || '[]')
					});
				try {
					finallyData = patches.reduce((doc, currentPatches, i) => {
						if (i === patches.length - 1) {
							// 跳过最近一次 patch 的 remove 操作
							currentPatches = currentPatches.filter(x => x.op !== 'remove');
						}
						return jsonPatch.applyPatch(doc, currentPatches).newDocument;
					}, finallyData);
					/**
					 * 标记最近一次 patches 修改
					 */
					const lastPatches = patches[patches.length - 1] || [];
					lastPatches.forEach(({ op, path: p }) => {
						const fieldPath = p
							.slice(1)
							.split('/')
							.map(jsonPatch.unescapePathComponent);
						if (!path(fieldPath, finallyData)) {
							console.error(
								'mark patch changes faild: cannot get filed path',
								fieldPath
							);
						}

						if (
							fieldPath.length % 2 === 0 &&
							fieldPath.slice(-2)[0] !== 'children'
						) {
							// 不标记非页面修改的操作
							return;
						}

						const isBatchOp =
							fieldPath.length % 2 !== 0 && fieldPath.slice(-1)[0] === 'children';
						for (let lastIdx = fieldPath.length - 1, i = lastIdx; i >= 0; i--) {
							if (i % 2 === 0) {
								if (isBatchOp && i === lastIdx) {
									const children = path(fieldPath, finallyData) || {};
									Object.values(children).forEach(x => {
										x.change = op === 'remove' ? 'add' : 'remove';
									});
								}
								continue;
							}
							let opReversed;
							if (i === lastIdx) {
								// 反转操作类型，因为存的时候是存的反向的
								opReversed = op === 'remove' ? 'add' : 'remove';
								// opReversed = op
							} else {
								opReversed = 'change';
							}
							finallyData = assocPath(
								fieldPath.slice(0, i + 1).concat('change'),
								opReversed,
								finallyData
							);
						}
						finallyData.change = 'change';
					});
				} catch (error) {
					console.error(error);
				}
			}

			// 漏洞扫描告警URL，添加到树对应节点

			const enableMonitor = data?.site_portrait?.enable;
			if (data?.warnings.length) {
				const affectMap = data?.warnings.reduce((obj, x) => {
					const affects = x?.affects;
					if (!affects) return obj;
					obj[affects] = obj[affects] || [];
					obj[affects].push(x);
					return obj;
				}, {});

				Object.entries(affectMap).forEach(([url, warnings]) => {
					fillWarnings({ url, warnings, tree: finallyData });
				});
			}
			let routeCount = 0;
			let abnormalRouteCount = 0;
			let changesCount = 0;

			const queue = [finallyData];
			while (queue.length) {
				const item = queue.shift();
				if (item.isLeaf) {
					routeCount += 1;
				}
				if (item.change === 'add') {
					changesCount += 1;
				}
				if (item.warnings) {
					abnormalRouteCount += 1;
				}
				if (item.children) {
					queue.push(...Object.values(item.children));
				}
			}
			// sitePortraitData?
			setSitePortraitData({
				patchSelectOptions,
				routeCount,
				abnormalRouteCount,
				changesCount,
				data: finallyData,
				enableMonitor,
			})
		}).finally(() => {
			setLoading(false)
		})
	}
	useEffect(() => {
		getJobList()
	}, [])
	useEffect(() => {
		if (jobId) {
			getSitePortrait('')
		} else {
			getJobList()
		}
	}, [jobId])

	return (
		<Loading
			show={baseData.loading && loading}
			hasData={baseData.data}
		>
			<div
				className={cn(
					className,
					s.sitePortrait,
					preview && s.noPadding,
					s.sitePortraitBg
				)}
				// ref={jobId}
			>
				{!preview && (
					<Breadcrumb className={s.p0}>
						<Item key="" active>
							网站画像
						</Item>
					</Breadcrumb>
				)}
				<div className={cn(s.sitePortraitContent, preview && s.noPadding)}>
					{!preview && (
						<>
							<div className={s.headerToolbox}>
								<Label className={s.labelOverview}>概况</Label>
								<div className='df aic'>
									<Select
										className={s.domainSelect}
										placeholder="请选择域名"
										value={[jobId]}
										onChange={({ selectedItem }) => handleSelectJob(selectedItem[0].value)}
										option={jobList}
									/>
									<Select
										className={s.patchSelect}
										placeholder="请选择监测完成时间"
										value={[selectedPatchVersion]}
										onChange={({ selectedItem }) => {
											const v = selectedItem[0].value
											setSelectPatch(v)
											getSitePortrait(v)
										}}
										option={sitePortraitData?.patchSelectOptions?.map((x, i) => {
											return { label: i === 0 ? '最新' : datetime(x.created_at), value: x.version}
										})}
									/>
								</div>
							</div>
							<div className={s.stats}>
								<MonitorCounts
									className={s.statsBlock}
									routeCount={sitePortraitData?.routeCount}
									abnormalRouteCount={sitePortraitData?.abnormalRouteCount}
									changesCount={sitePortraitData?.changesCount}
								/>
							</div>
							<Label className={s.labelDetail}>网站画像详情</Label>
						</>
					)}
					<main className={s.content}>
						<header className={s.title}>
							网站画像路径架构图
							<Tooltip
								placement="top"
								overlay="根据云防御防护日志分析聚合，智能监测网站画像情况"
							>
								<HelpIcon className={s.helpIcon} />
							</Tooltip>
						</header>
						<PortraitVis
							className={s.vis}
							key={selectedPatchVersion}
							data={sitePortraitData?.data}
							enableSitePortraitTriggerMonitor={
								sitePortraitData?.job?.vulSettings
									?.enableSitePortraitTriggerMonitor
							}
							enableMonitor={sitePortraitData?.enableMonitor}
							preview={preview}
							changeCount={sitePortraitData?.changesCount}
							abnormalCount={sitePortraitData?.abnormalRouteCount}
							balance={baseData?.data?.BalanceOne?.balance || {}}
						/>
					</main>
				</div>
			</div>
		</Loading>
	);
}

SitePortrait.propTypes = {
	className: PropTypes.string,
	preview: PropTypes.bool,
	match: PropTypes.shape({
		params: PropTypes.shape({
			jobId: PropTypes.string,
		}),
	}).isRequired,
	history: PropTypes.shape({
		replace: PropTypes.func.isRequired,
	}).isRequired,
};

SitePortrait.defaultProps = {
	className: s.sitePortrait,
	preview: false,
};

export default withRouter(SitePortrait);
